<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>图片懒加载 | yanyan</title>
    <meta name="description" content="Yan&#39;s blog">
    <meta name="generator" content="VuePress 1.4.0">
    <script src="https://cdn.bootcss.com/jquery/3.5.0/jquery.slim.min.js"></script>
  <script src="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.min.css">
  <link rel="shortcut icon" type="image/x-icon" href="./favicon.ico">
    
    <link rel="preload" href="/assets/css/0.styles.e1b3f17d.css" as="style"><link rel="preload" href="/assets/js/app.e358a08d.js" as="script"><link rel="preload" href="/assets/js/2.88fa18d1.js" as="script"><link rel="preload" href="/assets/js/19.6da24791.js" as="script"><link rel="prefetch" href="/assets/js/10.23baf844.js"><link rel="prefetch" href="/assets/js/11.45c148ba.js"><link rel="prefetch" href="/assets/js/12.e5930132.js"><link rel="prefetch" href="/assets/js/13.0547cd14.js"><link rel="prefetch" href="/assets/js/14.3e67795b.js"><link rel="prefetch" href="/assets/js/15.51129890.js"><link rel="prefetch" href="/assets/js/16.6987f89d.js"><link rel="prefetch" href="/assets/js/17.2807cff5.js"><link rel="prefetch" href="/assets/js/18.855e1707.js"><link rel="prefetch" href="/assets/js/20.e24d4aef.js"><link rel="prefetch" href="/assets/js/21.6efc6fba.js"><link rel="prefetch" href="/assets/js/22.10447f0f.js"><link rel="prefetch" href="/assets/js/23.9154cc24.js"><link rel="prefetch" href="/assets/js/24.9ad529fc.js"><link rel="prefetch" href="/assets/js/25.4c092e0a.js"><link rel="prefetch" href="/assets/js/26.debdaa01.js"><link rel="prefetch" href="/assets/js/27.8b90b660.js"><link rel="prefetch" href="/assets/js/28.1a323e01.js"><link rel="prefetch" href="/assets/js/29.6f108fc9.js"><link rel="prefetch" href="/assets/js/3.7210d3aa.js"><link rel="prefetch" href="/assets/js/30.e7df1937.js"><link rel="prefetch" href="/assets/js/31.2cb3120f.js"><link rel="prefetch" href="/assets/js/32.eb64932c.js"><link rel="prefetch" href="/assets/js/33.cac3e2f0.js"><link rel="prefetch" href="/assets/js/34.19ea35c4.js"><link rel="prefetch" href="/assets/js/35.fadf5d03.js"><link rel="prefetch" href="/assets/js/36.88b681f1.js"><link rel="prefetch" href="/assets/js/37.2a799db9.js"><link rel="prefetch" href="/assets/js/38.2741a2bf.js"><link rel="prefetch" href="/assets/js/39.359ceb72.js"><link rel="prefetch" href="/assets/js/4.9e938666.js"><link rel="prefetch" href="/assets/js/40.56fd4a10.js"><link rel="prefetch" href="/assets/js/41.e72117ad.js"><link rel="prefetch" href="/assets/js/42.63a6e190.js"><link rel="prefetch" href="/assets/js/43.c8072421.js"><link rel="prefetch" href="/assets/js/44.84cd8367.js"><link rel="prefetch" href="/assets/js/45.0ac810b0.js"><link rel="prefetch" href="/assets/js/46.bb83ff34.js"><link rel="prefetch" href="/assets/js/47.a9333a81.js"><link rel="prefetch" href="/assets/js/48.526b5494.js"><link rel="prefetch" href="/assets/js/49.73b61cc6.js"><link rel="prefetch" href="/assets/js/5.88b252c7.js"><link rel="prefetch" href="/assets/js/50.f34ab799.js"><link rel="prefetch" href="/assets/js/51.d06a49d9.js"><link rel="prefetch" href="/assets/js/52.348d5482.js"><link rel="prefetch" href="/assets/js/6.0face56b.js"><link rel="prefetch" href="/assets/js/7.31eca58d.js"><link rel="prefetch" href="/assets/js/8.69e9ce95.js"><link rel="prefetch" href="/assets/js/9.f25df9e1.js">
    <link rel="stylesheet" href="/assets/css/0.styles.e1b3f17d.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">yanyan</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <!----></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><!---->  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><a href="/html/001" class="sidebar-heading clickable"><span>HTML</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/css/image" class="sidebar-heading clickable"><span>CSS</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js/001" class="sidebar-heading clickable"><span>JS-基础</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-v8/001" class="sidebar-heading clickable"><span>JS-V8引擎原理 </span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/js-async/001" class="sidebar-heading clickable"><span>JS-异步I/O及异步编程</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/http/000" class="sidebar-heading clickable"><span>HTTP</span> <span class="arrow right"></span></a> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><a href="/extend/002" class="sidebar-heading clickable open"><span>拓展阅读</span> <span class="arrow down"></span></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/extend/002.html" class="sidebar-link">01.实现一个简单的Webpack</a></li><li><a href="/extend/001.html" class="sidebar-link">02.Nginx</a></li><li><a href="/extend/003.html" class="sidebar-link">03.vue router源码</a></li><li><a href="/extend/004.html" class="sidebar-link">04.防抖、节流</a></li><li><a href="/extend/005.html" class="active sidebar-link">05.图片懒加载</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/extend/005.html#方案一-clientheight、scrolltop-和-offsettop" class="sidebar-link">方案一:clientHeight、scrollTop 和 offsetTop</a></li><li class="sidebar-sub-header"><a href="/extend/005.html#方案二：getboundingclientrect" class="sidebar-link">方案二：getBoundingClientRect</a></li><li class="sidebar-sub-header"><a href="/extend/005.html#方案三-intersectionobserver" class="sidebar-link">方案三: IntersectionObserver</a></li></ul></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="图片懒加载"><a href="#图片懒加载" class="header-anchor">#</a> 图片懒加载</h1> <h2 id="方案一-clientheight、scrolltop-和-offsettop"><a href="#方案一-clientheight、scrolltop-和-offsettop" class="header-anchor">#</a> 方案一:clientHeight、scrollTop 和 offsetTop</h2> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>default.jpg<span class="token punctuation">&quot;</span></span> <span class="token attr-name">data-src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>http://www.xxx.com/target.jpg<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>img</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p>接着，通过监听 scroll 事件来判断图片是否到达视口:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">let</span> img <span class="token operator">=</span> document<span class="token punctuation">.</span>document<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">&quot;img&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token comment">//计数器，从第一张图片开始计</span>

<span class="token function">lazyload</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//首次加载别忘了显示图片</span>

window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> lazyload<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">lazyload</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> viewHeight <span class="token operator">=</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>clientHeight<span class="token punctuation">;</span><span class="token comment">//视口高度</span>
  <span class="token keyword">let</span> scrollTop <span class="token operator">=</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>scrollTop <span class="token operator">||</span> document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>scrollTop<span class="token punctuation">;</span><span class="token comment">//滚动条卷去的高度</span>
  <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> count<span class="token punctuation">;</span> i <span class="token operator">&lt;</span>num<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 元素现在已经出现在视口中</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>offsetTop <span class="token operator">&lt;</span> scrollHeight <span class="token operator">+</span> viewHeight<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span><span class="token punctuation">(</span>img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">&quot;src&quot;</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token string">&quot;default.jpg&quot;</span><span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span>
      img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>src <span class="token operator">=</span> img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">&quot;data-src&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      count <span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>当然，最好对 scroll 事件做节流处理，以免频繁触发:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// throttle函数我们上节已经实现</span>
window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'scroll'</span><span class="token punctuation">,</span> <span class="token function">throttle</span><span class="token punctuation">(</span>lazyload<span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h2 id="方案二：getboundingclientrect"><a href="#方案二：getboundingclientrect" class="header-anchor">#</a> 方案二：getBoundingClientRect</h2> <p>现在我们用另外一种方式来判断图片是否出现在了当前视口, 即 DOM 元素的 getBoundingClientRect API。</p> <p>上述的 lazyload 函数改成下面这样:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">lazyload</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> count<span class="token punctuation">;</span> i <span class="token operator">&lt;</span>num<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 元素现在已经出现在视口中</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getBoundingClientRect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>top <span class="token operator">&lt;</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>clientHeight<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span><span class="token punctuation">(</span>img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">&quot;src&quot;</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token string">&quot;default.jpg&quot;</span><span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span>
      img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>src <span class="token operator">=</span> img<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">&quot;data-src&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      count <span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="方案三-intersectionobserver"><a href="#方案三-intersectionobserver" class="header-anchor">#</a> 方案三: IntersectionObserver</h2> <p>这是浏览器内置的一个API，实现了监听window的scroll事件、判断是否在视口中以及节流三大功能。</p> <p>我们来具体试一把：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">let</span> img <span class="token operator">=</span> document<span class="token punctuation">.</span>document<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">&quot;img&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">IntersectionObserver</span><span class="token punctuation">(</span><span class="token parameter">changes</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">//changes 是被观察的元素集合</span>
  <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> len <span class="token operator">=</span> changes<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> change <span class="token operator">=</span> changes<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token comment">// 通过这个属性判断是否在视口中</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>change<span class="token punctuation">.</span>isIntersecting<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> imgElement <span class="token operator">=</span> change<span class="token punctuation">.</span>target<span class="token punctuation">;</span>
      imgElement<span class="token punctuation">.</span>src <span class="token operator">=</span> imgElement<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">&quot;data-src&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      observer<span class="token punctuation">.</span><span class="token function">unobserve</span><span class="token punctuation">(</span>imgElement<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span>img<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>这样就很方便地实现了图片懒加载，当然这个IntersectionObserver也可以用作其他资源的预加载，功能非常强大。</p></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/extend/004.html" class="prev">
        04.防抖、节流
      </a></span> <!----></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/assets/js/app.e358a08d.js" defer></script><script src="/assets/js/2.88fa18d1.js" defer></script><script src="/assets/js/19.6da24791.js" defer></script>
  </body>
</html>
